package com.ihr360.expr.utils;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.TimeZone;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;


/**
 * @author tom
 * @date 17-11-1
 */
public class DateUtils {

    public final static String STANDARD_DATE_FORMAT_UTC = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'";
    public final static String STANDARD_DATE_FORMAT_YMDHMS = "yyyy-MM-dd HH:mm:ss";
    public final static String STANDARD_DATE_FORMAT_YMDHM = "yyyy-MM-dd HH:mm";
    public final static String STANDARD_DATE_FORMAT_YMD = "yyyy-MM-dd";
    public final static String STANDARD_DATE_FORMAT_YM1 = "yyyy-MM";
    public final static String STANDARD_DATE_FORMAT_YM2 = "yyyy/MM";
    public final static String STANDARD_DATE_FORMAT_HMS = "HH:mm:ss";
    public final static String STANDARD_DATE_FORMAT_HM = "HH:mm";

    private static ThreadLocal<Map<String, SimpleDateFormat>> formatTimeThread = new ThreadLocal<>();
    private static Map<String, String> PATTERN_DATE_FORMAT_MAP = new HashMap<>();


    static {
        PATTERN_DATE_FORMAT_MAP.put("^([0-9]{4}-[0-9])$", STANDARD_DATE_FORMAT_YM1);
        PATTERN_DATE_FORMAT_MAP.put("^([0-9]{4}/[0-9])$", STANDARD_DATE_FORMAT_YM2);
        PATTERN_DATE_FORMAT_MAP.put("^([0-9]{4}-[0-9]{2})$", STANDARD_DATE_FORMAT_YM1);
        PATTERN_DATE_FORMAT_MAP.put("^([0-9]{4}/[0-9]{2})$", STANDARD_DATE_FORMAT_YM2);
        PATTERN_DATE_FORMAT_MAP.put("^([0-9]{4}-[0-9]{2}-[0-9]{2})$", STANDARD_DATE_FORMAT_YMD);
        PATTERN_DATE_FORMAT_MAP.put("^([0-9]{2}[:][0-9]{2})$", STANDARD_DATE_FORMAT_HM);
        PATTERN_DATE_FORMAT_MAP.put("^([0-9]{2}[:][0-9]{2}[:][0-9]{2})$", STANDARD_DATE_FORMAT_HMS);
        PATTERN_DATE_FORMAT_MAP.put("^([0-9]{4}-[0-9]{2}-[0-9]{2}[ ][0-9]{2}[:][0-9]{2})$", STANDARD_DATE_FORMAT_YMDHM);
        PATTERN_DATE_FORMAT_MAP.put("^([0-9]{4}-[0-9]{2}-[0-9]{2}[ ][0-9]{2}[:][0-9]{2}[:][0-9]{2})$", STANDARD_DATE_FORMAT_YMDHMS);
        PATTERN_DATE_FORMAT_MAP.put("^([0-9]{4}-[0-9]{2}-[0-9]{2}[ ][0-9]{2}[:][0-9]{2}[:][0-9]{2}\\.[0-9]{1})$", STANDARD_DATE_FORMAT_YMDHMS);
    }

    public static Date parse(String dateStr) {
        Pattern pattern = Pattern.compile("[0-9]*");
        Matcher isNum = pattern.matcher(dateStr);
        if (isNum.matches() && dateStr.length() == 13) {
            return new Date(Long.valueOf(dateStr));
        }
        if (dateStr==null || dateStr.trim().length()==0) {
            throw new RuntimeException("Parse date string empty!");
        }
        if (dateStr.contains("T") && dateStr.contains("Z")) {
            return parseFormatUTC(dateStr);
        }
        String dateFormat = null;
        for (Map.Entry<String, String> dfp : PATTERN_DATE_FORMAT_MAP.entrySet()) {
            if (Pattern.compile(dfp.getKey()).matcher(dateStr).matches()) {
                dateFormat = dfp.getValue();
                break;
            }
        }
        if (dateFormat != null) {
            return dateFormat(dateStr, dateFormat);
        }
        return null;
    }

    public static Date dateFormat(String dateStr, String pattern) {
        try {
            return getSimpleDateFormat(pattern).parse(dateStr);
        } catch (ParseException e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }

    }

    public static Date dateFormatYMD(Date date) {
        return dateFormat(date, STANDARD_DATE_FORMAT_YMD);
    }

    public static Date dateFormat(Date date, String pattern) {
        SimpleDateFormat simpleDateFormat = getSimpleDateFormat(pattern);
        String dateStr = simpleDateFormat.format(date);
        try {
            return simpleDateFormat.parse(dateStr);
        } catch (ParseException e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }

    public static String formatDateStr(Date date, String pattern) {
        SimpleDateFormat simpleDateFormat = getSimpleDateFormat(pattern);
        return simpleDateFormat.format(date);
    }

    private static Date parseFormatUTC(String dateStr) {
        SimpleDateFormat simpleDateFormat = getSimpleDateFormat(DateUtils.STANDARD_DATE_FORMAT_UTC);
        simpleDateFormat.setTimeZone(TimeZone.getTimeZone("GMT"));
        try {
            return simpleDateFormat.parse(dateStr);
        } catch (ParseException e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }

    private static SimpleDateFormat getSimpleDateFormat(String pattern) {
        Map<String, SimpleDateFormat> simpleDateFormatMap = formatTimeThread.get();
        if (simpleDateFormatMap == null) {
            simpleDateFormatMap = new HashMap<>();
            simpleDateFormatMap.put(pattern, new SimpleDateFormat(pattern));
        } else {
            SimpleDateFormat simpleDateFormat = simpleDateFormatMap.get(pattern);
            if (simpleDateFormat == null) {
                simpleDateFormatMap.put(pattern, new SimpleDateFormat(pattern));
            }
        }
        formatTimeThread.set(simpleDateFormatMap);
        return simpleDateFormatMap.get(pattern);
    }


    /**
     * 获得这天最小时间
     *
     * @param date
     * @return
     */
    public static Date getMinDateInDay(Date date) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.set(calendar.get(Calendar.YEAR), calendar.get(Calendar.MONTH), calendar.get(Calendar.DAY_OF_MONTH), 0, 0, 0);
        calendar.set(Calendar.MILLISECOND, 000);
        return calendar.getTime();
    }

    /**
     * 获得这天最大时间
     *
     * @param date
     * @return
     */
    public static Date getMaxDateInDay(Date date) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.set(calendar.get(Calendar.YEAR), calendar.get(Calendar.MONTH), calendar.get(Calendar.DAY_OF_MONTH), 23, 59, 59);
        calendar.set(Calendar.MILLISECOND, 999);
        return calendar.getTime();
    }

    /**
     * 返回两个时间中间的天数
     *
     * @param firstDate
     * @param secondDate
     * @return
     */
    public static int DateDiff(Date firstDate, Date secondDate) {
        long diffInMillies = Math.abs(secondDate.getTime() - firstDate.getTime());
        long diff = TimeUnit.DAYS.convert(diffInMillies, TimeUnit.MILLISECONDS);
        return Long.valueOf(diff).intValue();
    }

    /**
     * 判断日期是否在范围内，包含相等的日期
     */
    public static boolean isBetween(Date date, Date start, Date end) {
        if (date == null || start == null || end == null || start.after(end)) {
            throw new IllegalArgumentException("some date parameters is null or dateBegin after dateEnd");
        }
        return !date.before(start) && !date.after(end);
    }

    /**
     * 是否是同一天
     */
    public static boolean isSameDay(Date date1, final Date date2) {
        if (date1 != null && date2 != null) {
            Calendar cal1 = Calendar.getInstance();
            cal1.setTime(date1);
            Calendar cal2 = Calendar.getInstance();
            cal2.setTime(date2);
            return isSameDay(cal1, cal2);
        } else {
            throw new IllegalArgumentException("The date must not be null");
        }
    }

    public static boolean isSameDay(Calendar cal1, Calendar cal2) {
        if (cal1 != null && cal2 != null) {
            return cal1.get(0) == cal2.get(0) && cal1.get(1) == cal2.get(1) && cal1.get(6) == cal2.get(6);
        } else {
            throw new IllegalArgumentException("The date must not be null");
        }
    }

    /**
     * 是否同一个月
     */
    public static boolean isSameMonth(Date date1, Date date2) {
        if (date1 != null && date2 != null) {
            return dateFormat(date1, STANDARD_DATE_FORMAT_YM1).equals(dateFormat(date2, STANDARD_DATE_FORMAT_YM1));
        } else {
            throw new IllegalArgumentException("The date must not be null");
        }
    }

    /**
     * 获取当前月第一天
     * */
    public static Date getFirstDayInMonth(Date date){
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.set(Calendar.DAY_OF_MONTH,
                calendar.getActualMinimum(Calendar.DAY_OF_MONTH));
        calendar.set(Calendar.SECOND, 0);
        calendar.set(Calendar.HOUR_OF_DAY, 0);
        calendar.set(Calendar.MINUTE, 0);
        return calendar.getTime();
    }

    /**
     * 获取当前月最后一天
     * */
    public static Date getLastDayInMonth(Date date){
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.set(Calendar.DAY_OF_MONTH,calendar.getActualMaximum(
                Calendar.DAY_OF_MONTH));
        calendar.set(Calendar.SECOND, 59);
        calendar.set(Calendar.HOUR_OF_DAY, 23);
        calendar.set(Calendar.MINUTE, 59);
        return calendar.getTime();
    }

}

